#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>
#include <assert.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>


#define NUM 1024
#define SIZE 32
#define SEP " " //char *strtok(char *str, const char *delim);

//保存完整的命令行字符串
char cmd_line[NUM]={0};
//保存打散后的命令行字符串
char *g_argv[SIZE];


//写一个环境变量buffer[],用来测试
char g_myval[64];

#define NPUT_REDIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3
#define NONE_REDIR 0

int fd_status=NONE_REDIR;
char* Find_File(char*start)
{
    assert(start);
    char*end=start+strlen(start)-1; 
    while(end>=start)
    {
        if(*(end)=='>')
        {
            if(*(end-1)=='>')
            {
                fd_status=APPEND_REDIR;
                *(end-1)='\0';
                end++;
                break;
            }
            fd_status=OUTPUT_REDIR;
            *end='\0';
            end++;
            break;
        }
        else if(*(end)=='<')
        {
            fd_status=NPUT_REDIR;
            *end='\0';
            end++;
            break;
        }
        else 
        {
            end--;
        }
    }
    if(end>=start)
    {
        return end;
    }
    else 
    {
        return NULL;
    }

}

//shell 运行原理:通过让子进程执行命令，父进程等待&&解析命令
int main()
{
    //命令行解释器，一定是一个常驻内存的进程，不退出
  while(1)
  {
      //1.打印出提示信息"[root@VM-4-14-centos ~]#"
      printf("[root@VM-4-14-centos ~]#");
      fflush(stdout);
      memset(cmd_line,'\0',sizeof cmd_line);

      //2.获取用户在键盘上的输入(输入的是各种指令和选项"ls  -l  -a")
       if(fgets(cmd_line,sizeof cmd_line,stdin)==NULL)
       {
           continue;
       }
     // printf("%d\n",strlen(cmd_line));//ls -a'\n'
      cmd_line[strlen(cmd_line)-1]='\0';//将'\n'变为'\0'
    // printf("echo:%s\n",cmd_line);
    
      //分析是否有重定向，找到文件
      char* set=Find_File(cmd_line);
    
    
    //3.命令行字符串的解析:"ls -l -a -i"->"ls" "-l" "-a" "-i"
     g_argv[0]=strtok(cmd_line,SEP);//第一次调用，要传入原始字符串
   

     int index=1;
     if(strcmp(g_argv[0],"ls")==0)
     {
         g_argv[index++]="--color=auto";
     }

     if(strcmp(g_argv[0],"ll")==0)
     {
         g_argv[0]="ls";
         g_argv[index++]="-l";
         g_argv[index++]="--color=auto";
     }

     while(g_argv[index++]=strtok(NULL,SEP));//第二次,如果还要解析字符串，要传入NULL
     if(strcmp(g_argv[0],"export")==0&&g_argv[1]!=NULL)
     {
        strcpy(g_myval,g_argv[1]);
        int ret=putenv(g_myval);
        if(ret==0)printf("%s export success\n",g_argv[1]);
        continue;
     }


     //debug
     //for(index=0;g_argv[index];index++)
     //{
     //    printf("g_argv[%d]:%s\n",index,g_argv[index]);
     //}

    //4.TODO内置命令，让父进程自己执行的命令，我们叫内置命令，内建命令
    //内建命令本质是shell中的一个函数调用
    if(strcmp(g_argv[0],"cd")==0)
    {
      if(g_argv[1]!=NULL)
      chdir(g_argv[1]); //  int chdir(const char *path);

      continue;
    }

    //fork()
    pid_t id=fork();
    if(id==0)
    {
       // printf("下面功能子进程进行\n");
       // printf("child, MYVAL: %s\n",getenv("MYVAL"));
       // printf("child, PATH: %s\n",getenv("PATH"));
        
        if(set!=NULL)
        {
            //说明有重定向
            int fd=-1;
            switch(fd_status)
            {
                case NPUT_REDIR:
                     fd=open(set,O_RSYNC,0666);
                     dup2(fd,0);
                     break;
                case OUTPUT_REDIR:
                     fd=open(set,O_WRONLY | O_TRUNC | O_CREAT,0666);
                     dup2(fd,1);
                     break;
                case APPEND_REDIR:
                     fd=open(set,O_WRONLY | O_APPEND | O_CREAT,0666);
                     dup2(fd,1);
                     break;
                default:
                     printf("bug\n");
                     break;
            }
        }

        execvp(g_argv[0],g_argv);
        exit(1);
    }
    else
    {
        int status=0;
        pid_t ret =waitpid(id,&status,0);
        if(ret>0)
            printf("exit code:%d\n",WEXITSTATUS(status));
    }
     
  }
}
